package zzu.xjc.http;

import zzu.xjc.http.util.Logger;

import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

public class DirectBufferPool implements BufferPool{
    private final LinkedList<ByteBuffer> freeList = new LinkedList<>();
    private final ReentrantLock lock = new ReentrantLock();
    private int size;

    private final static int InitPoolSize = 64;
    private final static int BufferSize = 1 << 10;
    private final static int HugePoolSize = 1 << 10;

    @Override
    public ByteBuffer allocate() {
        lock.lock();
        try {
            if (freeList.size() >= 1){
                ByteBuffer buffer = freeList.pop();
                return buffer.clear();
            }else{
                expand();
                ByteBuffer buffer = freeList.pop();
                return buffer.clear();
            }
        }finally {
            lock.unlock();
        }
    }

    @Override
    public LinkedList<ByteBuffer> allocate(int n) {
        LinkedList<ByteBuffer> list = new LinkedList<>();
        lock.lock();
        try{
            while (freeList.size() < n){
                Logger.info("Buffer Pool free: " + freeList.size() + ", need: " + n);
                expand();
            }
            for (int i = 0; i < n; i++) {
                ByteBuffer pop = freeList.pop();
                list.add(pop.clear());
            }
            return list;
        }finally {
            lock.unlock();
        }
    }

    @Override
    public void giveback(ByteBuffer buffer) {
        lock.lock();
        try{
            freeList.push(buffer);
        }finally {
            lock.unlock();
        }
    }

    @Override
    public void giveback(List<ByteBuffer> bufferList) {
        lock.lock();
        try{
            for (ByteBuffer buffer : bufferList) {
                freeList.push(buffer);
            }
        }finally {
            lock.unlock();
        }
    }

    @Override
    public int size() {
        return size;
    }

    DirectBufferPool(){
        this(InitPoolSize);
    }

    DirectBufferPool(int initSize){
        Logger.info("Direct Buffer Pool Start Construct");
        this.size = initSize;
        for (int i = 0; i < size; i++) {
            freeList.push(ByteBuffer.allocateDirect(BufferSize));
        }
        Logger.info("Direct Buffer Pool Finish Construct, Pool Size: " + initSize);
    }

    private void expand(){
        lock.lock();
        try{
            Logger.info("Direct Buffer Pool Start Expand Size, Now Pool Size : " + size);
            if (size >= HugePoolSize){
                for (int i = 0; i < HugePoolSize; i++) {
                    freeList.push(ByteBuffer.allocateDirect(BufferSize));
                }
                size += HugePoolSize;
            }else{
                for (int i = 0; i < size; i++) {
                    freeList.push(ByteBuffer.allocateDirect(BufferSize));
                }
                size *= 2;
            }
            Logger.info("Direct Buffer Pool Finish Expand Size, Now Pool Size : " + size);
        }finally {
            lock.unlock();
        }
    }

    private void release(ByteBuffer buffer){

    }
    @Override
    public String log(){
        return "Buffer Pool size : " + size + " , free : " + freeList.size() ;
    }
}
